package fi.bluepill.server.rest;

import fi.bluepill.server.AuthenticationException;
import fi.bluepill.server.model.UserAccount;
import org.codehaus.jackson.node.ObjectNode;
import org.owasp.esapi.ESAPI;
import org.owasp.esapi.crypto.CipherText;
import org.owasp.esapi.crypto.PlainText;
import org.owasp.esapi.errors.EncryptionException;
import org.owasp.esapi.reference.DefaultHTTPUtilities;
import rip.core.Transactional;

import javax.servlet.http.Cookie;
import java.util.UUID;

public class UserAccountService extends BluepillService {
    @Transactional
    public void create() {
        assertSecureChannel();

        UserAccountCreateMessage input = read(UserAccountCreateMessage.class);

        if (jpaExists("select ua from UserAccount ua where ua.username = :username", qp("username", input.getUsername()))) {
            error("Username already in use.");
            return;
        }

        UserAccount account = new UserAccount();
        account.setUsername(input.getUsername());
        account.setEmail(input.getEmail());
        account.setSalt(UUID.randomUUID().toString());

        try {
            account.setPasswordHash(ESAPI.encryptor().hash(input.getPassword(), account.getSalt()));
        } catch (EncryptionException e) {
            error("Failed to hash password");
            return;
        }

        jpa().persist(account);

        ObjectNode m = okMessage();
        m.put("uuid", account.getUuid());

        write(m);
    }

    public void login() {
        assertSecureChannel();

        UserAccountLoginMessage input = read(UserAccountLoginMessage.class);

        UserAccount user = jpaQuery(UserAccount.class, "select ua from UserAccount ua where ua.username = :username", qp("username", input.getUsername()));

        if (user == null) {
            throw new AuthenticationException("No user found");
        }

        try {
            if (!ESAPI.encryptor().hash(input.getPassword(), user.getSalt()).equals(user.getPasswordHash())) {
                throw new AuthenticationException("Passwords didn't match");
            }
        } catch (EncryptionException e) {
           throw new AuthenticationException("Encryption error", e);
        }

        long expires = System.currentTimeMillis() + (14 * 24 * 60 * 60 * 1000); //14 days

        ObjectNode authCookieJson = json().createObjectNode();
        authCookieJson.put("expires", expires);
        authCookieJson.put("uuid", user.getUuid());
        authCookieJson.put("ip", ipAddress());

        String encryptedState = null;

        try {
            PlainText plainText = new PlainText(authCookieJson.toString());
            CipherText cipherText = ESAPI.encryptor().encrypt(plainText);
            encryptedState = ESAPI.encoder().encodeForBase64(cipherText.asPortableSerializedByteArray(), false);
        } catch (EncryptionException e) {
            throw new AuthenticationException("Could not encrypt cookie", e);
        }

        Cookie authCookie = new Cookie("bluepillAuth", encryptedState);
        authCookie.setDomain(env("domain"));
        authCookie.setMaxAge((int) ((expires - System.currentTimeMillis()) / 1000));

        if (!isTestMode())
            authCookie.setSecure(true);

        authCookie.setPath("/");

        DefaultHTTPUtilities.getInstance().addCookie(super.response, authCookie);

        ObjectNode m = okMessage();

        write(m);
    }



}
